package repository

import (
	"errors"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Map"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Method"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/NULL"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/OS/ShellUtils"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Slice"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/String"
	"go-fox-edge-kernel-manager/component"
	"go-fox-edge-kernel-manager/utils/ProcessUtils"
	"strconv"
)

type repoLocalAppPortService struct {
}

func (e *repoLocalAppPortService) newServicePort() (int, error) {
	serviceStartConfig, err := component.ManageConfigService.GetConfigValueThis("serviceStartConfig")
	if err != nil {
		return 0, err
	}

	services := Map.GetSlice(serviceStartConfig, "services", make([]interface{}, 0))

	// 统计：已经分配给服务的端口
	allocPorts := make([]int, 0)
	for _, service := range services {
		appPort := Map.GetInt(service.(map[string]interface{}), "appPort", 0)
		if appPort == 0 {
			continue
		}

		allocPorts = append(allocPorts, appPort)
	}

	// 找出：尚未分配的端口
	appPort := 9200
	for true {
		appPort++

		// 检查：这个端口是否已经在Linux操作系统中，被分配给其他进程使用：
		//注意，该命令行返回可能为空，此时会报err，这是go的os/exec的bug，所以不检查err
		result, _ := ShellUtils.ExecuteShell("netstat -anp | grep " + strconv.Itoa(appPort))
		if len(result) != 0 {
			continue
		}

		// 检查：这个端口是否已经分配给其他服务（这些服务可能尚未启动，尚未占用操作系统的端口）
		if Slice.Has(allocPorts, appPort) {
			continue
		}

		return appPort, nil
	}

	return 0, errors.New("分配端口失败")
}

func (e *repoLocalAppPortService) getServicePort(applicationName, applicationType string, appPid int64) (int, error) {
	serviceStartConfig, err := component.ManageConfigService.GetConfigValueThis("serviceStartConfig")
	if err != nil {
		return 0, err
	}

	services := Map.GetSlice(serviceStartConfig, "services", make([]interface{}, 0))

	appMap := NULL.Type(make(map[string]interface{}))
	for _, value := range services {
		service := value.(map[string]interface{})

		// 找到匹配的项目
		appName := Map.GetString(service, "appName", "")
		appType := Map.GetString(service, "appType", "")

		if applicationName != appName || applicationType != appType {
			continue
		}

		appMap = service
		break
	}

	// 检查：配置是否存在
	if appMap != nil {
		// 检查：端口是否存在，不存在则分配端口
		if Map.Has(appMap, "appPort") {
			// 检查：端口是否已经被其他程序占用
			appPort := Map.GetInt(appMap, "appPort", 0)

			shellLineList, _ := ShellUtils.ExecuteShell("netstat -anp | grep " + String.ToString(appPort))
			if len(shellLineList) == 0 {
				return appPort, nil
			}

			// 检查：使用该端口的进程ID是否存在，并且被自己使用，那么就直接沿用该端口
			pid := ProcessUtils.FindPidByPort(appPort)
			if pid == appPid {
				return appPort, nil
			}

			// 如果已经被占用，则重新分配一个端口
			appPort, err = e.newServicePort()
			if err != nil {
				return 0, err
			}

			appMap["appMap"] = appPort
			component.ManageConfigService.SaveConfigValueThis("serviceStartConfig", serviceStartConfig)
			return appPort, nil
		} else {
			// 如果已经被占用，则重新分配一个端口
			appPort, err := e.newServicePort()
			if err != nil {
				return 0, err
			}

			appMap["appMap"] = appPort
			component.ManageConfigService.SaveConfigValueThis("serviceStartConfig", serviceStartConfig)
			return appPort, nil
		}
	} else {
		appPort, err := e.newServicePort()
		if err != nil {
			return 0, err
		}

		appMap = make(map[string]interface{})
		appMap["appName"] = applicationName
		appMap["appType"] = applicationType
		appMap["appPort"] = appPort

		services = append(services, appMap)

		component.ManageConfigService.SaveConfigValueThis("serviceStartConfig", serviceStartConfig)

		return appPort, nil
	}

}

func (e *repoLocalAppPortService) GetServicePort(appName, appType string) (int, error) {
	// 简单校验参数
	if Method.HasEmpty(appName, appType) {
		return 0, errors.New("参数不能为空:appName, appType")
	}

	iniFileInfoList := LocalAppConf.GetConfFileInfoList()
	ProcessUtils.ExtendAppStatus(iniFileInfoList)

	appPid := int64(0)
	appPort := 0
	for _, data := range iniFileInfoList {
		if appName != data["appName"] {
			continue
		}
		if appType != data["appType"] {
			continue
		}

		appPid = Map.GetInt64(data, "pid", 0)
		appPort = Map.GetInt(data, "appPort", 0)
		break
	}

	// 检查：如果命令行参数和进程ID都存在，那么检查这个是否是该进程占用该端口
	if appPort != 0 && appPid != 0 {
		// 检查：使用该端口的进程ID是否存在，并且被自己使用，那么就直接沿用该端口，如果不是，那么重新分配端口
		pid := ProcessUtils.FindPidByPort(appPort)

		if pid == 0 || pid != appPid {
			val, err := e.getServicePort(appName, appType, appPid)
			if err != nil {
				return 0, err
			}

			appPort = val
		}
	} else {
		// 重新分配端口
		value, err := e.getServicePort(appName, appType, appPid)
		if err != nil {
			return 0, err
		}
		appPort = value
	}

	return appPort, nil
}
